package com.pivotal.cloud.security.password;

import com.pivotal.cloud.crypto.Digester;
import com.pivotal.cloud.crypto.codec.Hex;
import com.pivotal.cloud.crypto.codec.Utf8;
import com.pivotal.cloud.crypto.keygen.BytesKeyGenerator;
import com.pivotal.cloud.crypto.keygen.KeyGenerators;
import com.pivotal.cloud.crypto.utils.EncodingUtil;

import java.security.MessageDigest;

/**
 * @className: StandardPasswordEncoder
 * @projectName: PivotalCloud项目
 * @module: PivotalCloud项目-StandardPasswordEncoder类，主要位于Security安全模块-Sha256标准模式密码加密模块
 * @content: StandardPasswordEncoder-Sha256标准模式密码加密
 * @author: Powered by Marklin
 * @datetime: 2025-06-24 13:24
 * @version: 1.0.0
 * @copyright: Copyright © 2018-2025 PivotalCloud Systems Incorporated. All rights
 * reserved.
 */
public class StandardPasswordEncoder implements PasswordEncoder {

	private static final int DEFAULT_ITERATIONS = 1024;

	private final Digester digester;

	private final byte[] secret;

	private final BytesKeyGenerator saltGenerator;

	/**
	 * Constructs a standard password encoder with no additional secret value.
	 */
	public StandardPasswordEncoder() {
		this("");
	}

	/**
	 * Constructs a standard password encoder with a secret value which is also included
	 * in the password hash.
	 * @param secret the secret key used in the encoding process (should not be shared)
	 */
	public StandardPasswordEncoder(CharSequence secret) {
		this("SHA-256", secret);
	}

	private StandardPasswordEncoder(String algorithm, CharSequence secret) {
		this.digester = new Digester(algorithm, DEFAULT_ITERATIONS);
		this.secret = Utf8.encode(secret);
		this.saltGenerator = KeyGenerators.secureRandom();
	}

	/**
	 * 密码加密
	 * @param rawPassword 原始密码
	 * @return 返回结果
	 */
	@Override
	public String encode(CharSequence rawPassword) {
		return encode(rawPassword, this.saltGenerator.generateKey());
	}

	/**
	 * 验证密码
	 * @param rawPassword 原始密码
	 * @param encodedPassword 加密密码
	 * @return 返回结果
	 */
	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		byte[] digested = decode(encodedPassword);
		byte[] salt = EncodingUtil.subArray(digested, 0, this.saltGenerator.getKeyLength());
		return MessageDigest.isEqual(digested, digest(rawPassword, salt));
	}

	private String encode(CharSequence rawPassword, byte[] salt) {
		byte[] digest = digest(rawPassword, salt);
		return new String(Hex.encode(digest));
	}

	private byte[] digest(CharSequence rawPassword, byte[] salt) {
		byte[] digest = this.digester.digest(EncodingUtil.concatenate(salt, this.secret, Utf8.encode(rawPassword)));
		return EncodingUtil.concatenate(salt, digest);
	}

	private byte[] decode(CharSequence encodedPassword) {
		return Hex.decode(encodedPassword);
	}

}
